#ifndef EMS_UTILS_JSON_H
#define EMS_UTILS_JSON_H

#include <string>
#include <vector>
#include <map>
#include <istream>
#include <memory>

namespace ems {
namespace utils {

class Json {
public:
    Json();
    Json(const Json& other);
    Json& operator=(const Json& other);
    ~Json();
    
    bool parse(const std::string& jsonString);
    bool parse(std::istream& stream);
    
    std::string asString() const;
    int asInt() const;
    double asDouble() const;
    bool asBool() const;
    
    std::string getString(const std::string& key, const std::string& defaultValue = "") const;
    int getInt(const std::string& key, int defaultValue = 0) const;
    double getDouble(const std::string& key, double defaultValue = 0.0) const;
    bool getBool(const std::string& key, bool defaultValue = false) const;
    
    Json getObject(const std::string& key) const;
    std::vector<Json> getArray(const std::string& key) const;
    
    std::map<std::string, Json> getMap() const;
    
    bool isNull() const;
    bool isString() const;
    bool isNumber() const;
    bool isBool() const;
    bool isObject() const;
    bool isArray() const;

private:
    struct JsonData;
    std::unique_ptr<JsonData> data_;
    
    void parseValue(const std::string& str, size_t& pos);
    void parseObject(const std::string& str, size_t& pos);
    void parseArray(const std::string& str, size_t& pos);
    void parseString(const std::string& str, size_t& pos);
    void parseNumber(const std::string& str, size_t& pos);
    void parseBool(const std::string& str, size_t& pos);
    void parseNull(const std::string& str, size_t& pos);
    
    void skipWhitespace(const std::string& str, size_t& pos) const;
    std::string unescapeString(const std::string& str) const;
};

}  // namespace utils
}  // namespace ems

#endif  // EMS_UTILS_JSON_H